package trab4.transactions;

import java.awt.Graphics2D;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import trab4.* ;
import trab4.grid.Grid;
import trab4.grid.GridOperations;
import trab4.time.* ;

abstract public class AbstractTransactionManager<T> implements Transactions {

	protected Clock clock ;
	protected Server owner ;
	
	protected AbstractTransactionManager( Server owner, Clock clock ) {
		this.owner = owner ;
		this.clock = clock ;
		this.tidCounter = owner.sid << 32 ; // high word keeps sid...
	}

	public long c_OpenTransaction() {
		throw new RuntimeException("Not implemented...") ;
	}

	public long d_OpenTransaction() {
		throw new RuntimeException("Not implemented...") ;		
	} 

	public void c_AbortTransaction( long tid) {
		throw new RuntimeException("Not implemented...") ;
	}

	public void d_AbortTransaction( long tid) {
		throw new RuntimeException("Not implemented...") ;
	}

	public TransactionResult c_CloseTransaction( long tid) {
		throw new RuntimeException("Not implemented...") ;
	}
	
	public TransactionResult d_CloseTransaction( long tid) {
		throw new RuntimeException("Not implemented...") ;
	}
	
	protected int displayGridSize( int units ) {
		return (int) Math.ceil( Math.sqrt( units ) ) ;
	}
	
	@SuppressWarnings("unchecked")
	public T getByTid( long tid ) {
		TransactionStub res = transactions.get( tid ) ;
		if( res == null ) {
			if( ownTid( tid ) ) 
				throw new RuntimeException("Unknown transaction tid...") ;
			res = new TransactionStub( tid, clock.increment().value(), owner.grid ) ;
			transactions.put( tid, res ) ;		
		}
		return (T) res ;
	}
	
	protected boolean ownTid( long tid ) {
		return tid >> 32 == owner.sid ;
	}
	
	protected SortedSet<TransactionStub> transactions() {
		return new TreeSet<TransactionStub>( transactions.values() ) ;
	}	
	
	// high word of a tid indicates the server that opened the transaction...
	// useful in a distributed transaction - a tid already includes the sid of the coordinator server.
	protected long tidCounter ; 
	protected Map<Long, TransactionStub> transactions = new HashMap<Long, TransactionStub>() ;
	
	public void display(Graphics2D gu, Graphics2D gs) {

		int N = displayGridSize( ServerDB.size() ) ;
		
		int Si = owner.sid % N, Sj = owner.sid / N ;
		double W = owner.grid.width(), H = owner.grid.height();

		Graphics2D gs2 = (Graphics2D)gs.create() ;	
			
			int rows = owner.grid.rows();
			int cols = owner.grid.cols();
			gs2.translate( 50 + Si * W * rows , 50 + Sj * H * cols) ;

			Set<TransactionStub> ts = transactions() ;
			for( int i = 0 ; i < rows ; i++ )
				for( int j = 0 ; j < cols ; j++ ) {
					int k = 0 ;
					for( TransactionStub t : ts )
						if( t.updated(i, j)) k++ ;				

					owner.grid.display(i, j, gs2, k ) ;
				}		
			gs2.dispose();
	}
}

class TransactionStub extends AbstractTransaction implements GridOperations {
	
	Grid committed ; // a reference to the committed version of the grid...
	Grid tentative ; // a tentative version of the grid maintained by this transaction...
	boolean cUpdated[][], sUpdated[][] ; // tells which grid cells were written to...
	
	TransactionStub( long tid, TimeStamp begin, Grid commited) {
		super( tid, begin ) ;
		this.committed = commited ;
		this.tentative = commited.makeCopy() ;
		this.cUpdated = new boolean[committed.rows()][ commited.cols() ] ;
		this.sUpdated = new boolean[committed.rows()][ commited.cols() ] ;
		for( int i = 0 ; i < commited.rows(); i++ )
			for( int j = 0 ; j < commited.cols(); j++ ) 
				cUpdated[i][j] = sUpdated[i][j] = false ;
	}
	
	//If the transaction is committed then written values are copied back to the committed version...
	public void commitChanges() {
		for( int i = 0 ; i < tentative.rows() ; i++ )
			for( int j = 0 ; j < tentative.cols() ; j++ ) {
				if( cUpdated[i][j] ) 
					committed.writeColor( tid, i, j,  tentative.readColor( tid, i, j)) ;
				if( sUpdated[i][j] ) 
					committed.writeShape( tid, i, j,  tentative.readShape( tid, i, j)) ;
			}
	}
	
	public boolean updated( int i, int j ) {
		return cUpdated[i][j] || sUpdated[i][j] ;
	}
	
	public int readColor( long tid, int i, int j) {		
		rOps.add("rC: "+ i + " " + j) ;
		return tentative.readColor( tid, i, j) ;
	}
 
	public int readShape( long tid, int i, int j) {
		rOps.add("rS: "+ i + " " + j + " ") ;
		return tentative.readShape( tid, i, j) ;
	}

	public void writeColor( long tid, int i, int j, int v) {
		tentative.writeColor(tid, i, j, v ) ;
		wOps.add("wC: "+ i + " " + j + " ") ;
		cUpdated[i][j] = true ;
	}

	public void writeShape( long tid, int i, int j, int v) {
		tentative.writeShape( tid, i, j, v ) ;
		wOps.add("wS: "+ i + " " + j + " ") ;
		sUpdated[i][j] = true ;
	}

	public int[] gridSize( long tid) {
		return committed.gridSize( tid) ;
	}

	Set<String> rOps = new HashSet<String>() ;
	Set<String> wOps = new HashSet<String>() ;
}